﻿using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using Internal.KuiHuaBaoDian.Services.Net;

namespace KuiHuaBaoDian.Services.Net {

    public interface INetService {

        public delegate IText GetPacketIDTextDelegate(INetPacketID packetID);

        public delegate IText GetPacketContentTextDelegate(INetPacket packet);

        public delegate bool SplitPacketBytesDelegate(byte[] packetBytes, out INetPacketID packetID, out byte[] packetBody);

        public delegate byte[] BuildPacketBytesDelegate(INetPacketID packetID, byte[] packetBody);

        public delegate void ConnectionStateChanged(NetConnectionHandle handle, INetConnection.Status status);

        bool IsConnected(NetConnectionHandle handle);

        UniTask Close(NetConnectionHandle handle);

        UniTask<NetConnectionHandle> Connect(string url, CancellationToken cancellationToken);

        void Update();

        GetPacketIDTextDelegate GetPacketIDTextFunc { get; set; }

        GetPacketContentTextDelegate GetPacketContentTextFunc { get; set; }

        SplitPacketBytesDelegate SplitPacketBytesFunc { get; set; }

        BuildPacketBytesDelegate BuildPacketBytesFunc { get; set; }

        event ConnectionStateChanged ConnectionStatusChanged;

        bool LogEnabled { get; set; }

        INetReceiver AsReceiver();

        INetSender AsSender();
    }

    /// <summary>
    /// 发送器
    /// </summary>
    public interface INetSender {

        /// <summary>
        /// 发送数据包
        /// </summary>
        /// <param name="handle">连接句柄</param>
        /// <param name="packet">数据包</param>
        /// <returns>异步任务</returns>
        UniTask Send(NetConnectionHandle handle, INetPacket packet);
    }

    /// <summary>
    /// 网络服务
    /// </summary>
    public static class NetService {

        /// <summary>
        /// 单例 
        /// </summary>
        public static INetService Instance => m_Instance ??= KHBD.TypeMap.Instantiate<INetService>();
        private static INetService m_Instance;
    }

    [Implement(typeof(INetService))]
    internal sealed class KHBD_NetService : INetService, IService, INetReceiver, INetSender {

        internal sealed record NetPacket(INetPacketID ID, byte[] Body) : INetPacket;

        public INetReceiver AsReceiver() => m_Receiver;

        public INetSender AsSender() => this;

        private readonly Dictionary<NetConnectionHandle, INetConnection> m_ConnectionDictionary = new();

        private readonly Queue<INetPacket> m_PacketQueue = new();
        private readonly object m_PacketQueueLocker = new();

        private readonly INetReceiver m_Receiver;
        private readonly INetReceiverInternal m_Dispatcher;

        public INetService.GetPacketIDTextDelegate GetPacketIDTextFunc { get; set; }

        public INetService.GetPacketContentTextDelegate GetPacketContentTextFunc { get; set; }

        public INetService.SplitPacketBytesDelegate SplitPacketBytesFunc { get; set; }

        public INetService.BuildPacketBytesDelegate BuildPacketBytesFunc { get; set; }

        public event INetService.ConnectionStateChanged ConnectionStatusChanged;

        public bool LogEnabled { get; set; }

        public KHBD_NetService() {
            m_Receiver = KHBD.TypeMap.Instantiate<INetReceiver>();
            m_Dispatcher = m_Receiver as INetReceiverInternal;
        }

        void IService.OnApplicationQuit() {
        }

        public async UniTask<NetConnectionHandle> Connect(string url, CancellationToken cancellationToken) {
            if (KHBD.Context.IsCancellationRequested(cancellationToken)) {
                UnityEngine.Debug.Log($"{nameof(NetService)} 连接取消 ");
                return NetConnectionHandle.None;
            }

            var connection = KHBD.TypeMap.Instantiate<INetConnection>();
            m_ConnectionDictionary.Add(connection.Handle, connection);
            connection.OnStatusChanged += (status) => {
                ConnectionStatusChanged?.Invoke(connection.Handle, status);
                if (status != INetConnection.Status.Success) {
                    Dispose(connection);
                    try {
                        if (KHBD.Context.IsApplicationPlaying) {
                            if (status == INetConnection.Status.Error) {
                                UnityEngine.Debug.LogWarning($"{nameof(NetService)} 连接断开 {nameof(status)}={status}");
                            } else {
                                UnityEngine.Debug.Log($"{nameof(NetService)} 连接断开 {nameof(status)}={status}");
                            }
                        }
                    } catch (ApplicationException e) {
                        UnityEngine.Debug.Log(e.Message);
                    }
                }
            };
            if (LogEnabled) {
                UnityEngine.Debug.Log($"{nameof(NetService)} 开始连接 [{connection.GetType().Name}] {url} ...");
            }
            await connection.Connect(url, cancellationToken);
            if (connection.IsConnected) {
                if (KHBD.Context.IsCancellationRequested(cancellationToken)) {
                    await connection.Close(false);
                    UnityEngine.Debug.Log($"{nameof(NetService)} 连接取消 ");
                    return NetConnectionHandle.None;
                }
                UnityEngine.Debug.Log($"{nameof(NetService)} 连接成功 ");
                DealPacket().Forget();
                return connection.Handle;
            } else {
                return NetConnectionHandle.None;
            }
        }

        public async UniTask Close(NetConnectionHandle handle) {
            if (m_ConnectionDictionary.TryGetValue(handle, out var connection)) {
                await connection.Close(true);
            }
        }

        private void Dispose(INetConnection connection) {
            if (connection != null) {
                m_ConnectionDictionary.Remove(connection.Handle);
                connection.Dispose();
            }

            if (m_ConnectionDictionary.Count == 0) {
                lock (m_PacketQueueLocker) {
                    m_PacketQueue.Clear();
                }
            }
        }

        async UniTask INetSender.Send(NetConnectionHandle handle, INetPacket packet) {
            if (packet != null && m_ConnectionDictionary.TryGetValue(handle, out var connection) && connection != null && connection.IsConnected) {
                var packetID = packet.ID;
                var packetBytes = BuildPacketBytesFunc.Invoke(packetID, packet.Body);
                if (LogEnabled) {
                    var packetIDText = GetPacketIDTextFunc?.Invoke(packetID);
                    var packetContentText = GetPacketContentTextFunc?.Invoke(packet);
                    UnityEngine.Debug.Log($"<color=#FF8000>发送</color>网络数据包[数据包ID({packetIDText?.ToString() ?? packetID.Value.ToString()})]\n{packetContentText}");
                }
                await connection.Send(packetBytes);
            }
        }

        public bool IsConnected(NetConnectionHandle handle) {
            if (m_ConnectionDictionary.TryGetValue(handle, out var connection)) {
                return connection.IsConnected;
            }
            return false;
        }

        public void Update() {
            while (m_PacketQueue.Count > 0 && KHBD.Context.IsApplicationPlaying) {
                var packet = default(INetPacket);
                lock (m_PacketQueueLocker) {
                    packet = m_PacketQueue.Dequeue();
                }
                Dispatch(packet);
            }
        }

        private bool m_HasDealTask = false;
        private async UniTaskVoid DealPacket() {
            if (m_HasDealTask) {
                return;
            }
            await KHBD.Context.WaitUntil(() => {
                m_HasDealTask = true;
                try {
                    foreach (var kv in m_ConnectionDictionary) {
                        var connection = kv.Value;
                        var packetBytes = connection.GetReceived();
                        if (packetBytes != null) {
                            Enqueue(packetBytes);
                        }
                    }
                } catch (InvalidOperationException) {
                }
                return m_ConnectionDictionary.Count == 0;
            });
            m_HasDealTask = false;
        }

        private void Enqueue(byte[] packet) {
            if (!SplitPacketBytesFunc.Invoke(packet, out var id, out var body)) {
                return;
            }
            lock (m_PacketQueueLocker) {
                m_PacketQueue.Enqueue(new NetPacket(id, body));
            }
        }

        private void Dispatch(INetPacket packet) {
            var packetID = packet.ID;
            if (LogEnabled) {
                var packetIDText = GetPacketIDTextFunc?.Invoke(packetID);
                var packetContentText = GetPacketContentTextFunc?.Invoke(packet);
                UnityEngine.Debug.Log($"<color=#FFD500>收到</color>网络数据包[数据包ID({packetIDText?.ToString() ?? packetID.Value.ToString()})]\n{packetContentText}");
            }
            m_Dispatcher.Dispatch(packet);
        }

        void INetReceiver.AddListener(INetPacketID packetID, NetCallbackID callbackID, Action<INetPacket> callback, bool isOneTime) {
            m_Receiver.AddListener(packetID, callbackID, callback, isOneTime);
        }

        void INetReceiver.RemoveListener(INetPacketID packetID, NetCallbackID callbackID) {
            m_Receiver.RemoveListener(packetID, callbackID);
        }
    }
}